#ifndef BASEGAMEENGINE_H
#define BASEGAMEENGINE_H

#include "Font.h"

// Prototype the class, so we can use pointers of type DisplayableObject*
class DisplayableObject;


class BaseGameEngine
{
public:
	/** Constructor. */
	BaseGameEngine(int iMaxObjects);

	/** Virtual destructor */
	virtual ~BaseGameEngine(void);

	//
	// Internal functions
	//

	/** Initialise the SDL and create a window of the designated size */
	int Initialise( int iScreenWidth, int iScreenHeight );

	/** Deinitialise everything required. */
	void Deinitialise(void);

	/** The main game loop - will call the draw and gamel logic functions in turn */
	int MainLoop(void);
	
	/** Destroy any existing displayable objects */
	void DestroyOldObjects();

	/**
	Gets the specified displayable object.
	You may need to dynamic_cast the resulting pointer to the correct type.
	*/
	DisplayableObject* GetDisplayableObject( int iIndex );

public:
	//
	// Utility functions: (call these)
	//

	/**
	Is the key with the specified code currently being pressed?
	Returns 1 for Yes, 0 for No.
	*/
	int IsKeyPressed(int iKeyCode);

	/**
	Function to get the time, in terms of the number of ticks.
	Can be used to decide which animation to display, or whether it's time to do something yet
	*/
	inline int GetTime() { return m_iTick; }

	/** Return true if it is time to act, false otherwise */
	inline bool TimeToAct() { return m_iTick >= m_iNextTickToActOn; }	

	/** Specify the next time at which to act - current time plus the delay, in ticks */
	inline void SetTimeToAct( int iDelay ) { m_iNextTickToActOn = m_iTick + iDelay; }

	/** Method to set a pixel colour on the screen, in-lined, but checks the supplied parameters */
	inline void SafeSetScreenPixel( int iX, int iY, unsigned int uiColour )
	{
		// Validate that the position is within the screen
		if ( ( iX < 0 ) || ( iX >= m_iIntsPerScreenRow ) || ( iY < 0 ) || ( iY >= m_iScreenHeight ) )
			return;
		// Set the value of the pixel
		((unsigned int *)m_pActualScreen->pixels)[iX + iY * m_iIntsPerScreenRow] = uiColour;
	}

	/** Method to set a pixel colour on the screen, fast and in-lined. */
	inline void SetScreenPixel( int iX, int iY, unsigned int uiColour )
	{
		// Set the value of the pixel - assuming that it is within the screen size
		((unsigned int *)m_pActualScreen->pixels)[iX + iY * m_iIntsPerScreenRow] = uiColour;
	}

	/** Copy all of the background buffer onto the screen */
	void CopyAllBackgroundBuffer();

	/** Copy the background pixels in the specified region onto the screen */
	void CopyBackgroundPixels( int iX, int iY, int iWidth, int iHeight );

	/** Indicate that either the screen or just specific regions of it must be redrawn */
	void Redraw( bool bAllScreen ) { m_bNeedsRedraw = true; if ( bAllScreen ) m_bWholeScreenUpdate = true; }

	/** Override to populate the relevant update rectangles for screen movement */
	virtual void GetUpdateRectanglesForChangingObjects();
	
	/**
	Get a pointer to an update rect structure to fill in.
	The designated rectangle on the screen will be updated in the next draw.
	*/
	SDL_Rect* GetNextUpdateRect();

	/** Method to set a pixel colour in the background buffer */
	inline void SetBackgroundPixel( int iX, int iY, unsigned int uiColour )
	{
		// Set the value of the pixel - assuming that it is within the screen size
		m_puiScreenBuffer[iX + iY * m_iIntsPerScreenRow] = uiColour;
	}

	/** Method to set a pixel colour on the screen, in-lined, but checks the supplied parameters */
	inline void SafeSetBackgroundPixel( int iX, int iY, unsigned int uiColour )
	{
		// Validate that the position is within the screen
		if ( ( iX < 0 ) || ( iX >= m_iIntsPerScreenRow ) || ( iY < 0 ) || ( iY >= m_iScreenHeight ) )
			return;
		// Set the value of the pixel - assuming that it is within the screen size
		m_puiScreenBuffer[iX + iY * m_iIntsPerScreenRow] = uiColour;
	}

	/** Method to fill the entire background with the same colour */
	virtual void FillBackground(unsigned int uiPixelColour);

	/** Screen width */
	int GetScreenWidth() { return m_iScreenWidth; }

	/** Screen height */
	int GetScreenHeight() { return m_iScreenHeight; }

	/** Set the exit code - effectively ending the program! */
	void SetExitWithCode( int iCode ) { m_iExitWithCode = iCode; }

	/** Obtain a pointer to the font object */
	virtual Font* GetFontObject() { return m_pFont; }

	/** Get a pointer to the SDL surface for the screen */
	SDL_Surface * GetSDLSurface() { return m_pActualScreen; }

	/** Draw a string */
	void DrawString(int iX, int iY, const char* pText, unsigned int uiColour )
	{
		m_pFont->DrawString(m_pActualScreen, iX, iY, pText, uiColour );
	}

	/** Set the update rectangle */
	void SetUpdateRect( int iX, int iY, int iWidth, int iHeight )
	{
		SDL_UpdateRect( m_pActualScreen, iX, iY, iWidth, iHeight );
	}


	/**
	Redraws the background behind each of the displayable objects, in their old positions.
	*/
	void UndrawChangingObjects();

public:
	//
	// Overridables:
	//
	/** Override if necessary - a key was released. See also IsKeyPressed() */
	virtual void KeyUp(int iKeyCode);
	/** Override if necessary - a key was pressed. See also IsKeyPressed() */
	virtual void KeyDown(int iKeyCode);
	/** Override if necessary - handle initialisation */
	virtual int GameInit(void);

	/**
	In here you need to create any movable objects that you wish to use.
	Sub-classes need to implement this function.
	*/
	virtual int InitialiseObjects();

	/** Override if necessary - handle cleanup - e.g. delete allocated objects */
	virtual void CleanUp(void);
	/** Override if necessary - render the screen. Base version delegates to DrawScreen() or DrawChangingObjects) as appropriate */
	virtual void GameRender();

	/**
	The game logic - move things and change the state if necessary.
	Must call Redraw(true/false) if anything changes that shows on the screen.
	*/
	virtual void GameAction();

	/** Draw the entire screen */
	virtual void DrawScreen(void);

	/** Draw just the changing objects */
	virtual void DrawChangingObjects();

	/** Draw text labels */
	virtual void DrawStrings();

	/**
	Do any setup of back buffer prior to locking the screen buffer.
	Basically do the drawing of the background in here and it'll be copied to the screen for you as needed.
	*/
	virtual void SetupBackgroundBuffer();

	/** Tell all displayable objects to update themselves. Calls DoUpdate on each displayable object. */
	virtual void UpdateAllObjects( int iCurrentTime );


private:
	/**
	If set to other than -1 the program will exit with this exit code.
	*/
	int m_iExitWithCode;

	/** Array of key status information saying what is currently pressed. See IsKeyPressed(). */
	int *m_pKeyStatus;

	/** Screen width, in pixels */
	int m_iScreenWidth;

	/** Screen height, in pixels */
	int m_iScreenHeight;

	/** Screen surface, the SDL structure. */
	SDL_Surface *m_pActualScreen;

	/** A buffer for the screen surface, to show the background, e.g. tiles. */
	unsigned int * m_puiScreenBuffer;

	/** Number of ints per row of the screen. */
	int m_iIntsPerScreenRow;

	/** Current tick/time */
	int m_iTick;

	/** Pointer to the font object, used to draw text */
	Font* m_pFont;

	/** true if screen needs a redraw, false if nothing has changed */
	bool m_bNeedsRedraw;

	/** True if whole screen should be updated on the next draw, false if only the moving objects should. */
	bool m_bWholeScreenUpdate;

	/** Max number of moving objects permitted */
	int m_iMaxObjects;
	/** Array of rectangles with update positions for moving objects */
	SDL_Rect* m_pUpdateRectangles;
	/** Count of number of update rectangles which have currently been filled */
	int m_iUpdateRectsInUse;

	/** The next tick to act on - to prevent continuous updates */
	int m_iNextTickToActOn;

protected:
	/** Array of displayable objects - expect it to be modified by sub-class(es) */
	DisplayableObject** m_ppDisplayableObjects;
};

#endif
